/* 词法分析器 辅助语法分析 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "common.h"

extern FILE* src;      // 代码源文件

char cur_char = ' ';   // 当前读到的字符
char line[80];         // 缓冲区
int head = 0, tail = 0;// 缓冲区的头指针和尾指针
bool legal_line;       // 用于跳过注释

char   word[norw][16]; // 保留字集合
symbol wsym[norw];     // 保留字 到 symbol
symbol ssym[256];      // 单个字符 到 symbol
 
char   cur_sym[16];    // 当前标识符或者保留字
int    cur_num;        // 当前数字
symbol cur_symbol;     // 当前单词的类别


// 从src中读入一个字符
static void getch()
{
    // 如果当前行读完了，就往下再读一行，填充line
    while (head == tail) {        

        assert(!feof(src), "read EOF\n");

        cur_char = ' ';
        tail = 0, head = 0;
        legal_line = true;

        // 读取一行，忽略注释'#'后的内容
        while(cur_char != '\n') {
            if(fscanf(src, "%c", &cur_char) == EOF) break;            
            if(cur_char == '#') legal_line = false;
            if(legal_line) line[tail++] = cur_char;
        }
        line[tail] = '\0';
        
        // 输出读到的行
        printf("%s",line); 
    }
    cur_char = line[head++];
}

// 当前字符是字母吗
static inline bool isABC()
{
    return ((cur_char >= 'a' && cur_char <= 'z') 
         || (cur_char >= 'A' && cur_char <= 'Z'));
}

// 当前字符是数字吗
static inline bool is123()
{
    return (cur_char >= '0' && cur_char <= '9');
}

// 设置cur_sym
static void set_sym()
{
    int index = 0;
    while(isABC() | is123()) {
        cur_sym[index++] = cur_char;
        getch();
    }
    assert(index <= 15, "Too long ident\n");
    cur_sym[index] = '\0';
}

// 设置cur_num
static void set_num()
{
    cur_num = 0;
    while(is123()) {
        cur_num = cur_num * 10 + cur_char - '0';
        getch();
    }
}

// 设置cur_symbol
static void set_symbol(int flag)
{
    switch(flag)
    {
        case 0: // 标识符或者保留字
            for(int i = 0; i < norw; i++) {
                if(strcmp(cur_sym, word[i]) == 0) {
                    cur_symbol = wsym[i];
                    return; 
                }
            }
            cur_symbol = ident;
            break;
        case 1: // 数字
            cur_symbol = number;
            break;
        case 2: // 赋值或等于
            getch();
            if(cur_char == '=') {
                cur_symbol = eql;
                getch();
            } else {
                cur_symbol = becomes;
            }
            break;
        case 3: // <= 或 <
            getch();
            if(cur_char == '=') {
                cur_symbol = leq;
                getch();
            } else {
                cur_symbol = lss;
            }
            break;
        case 4: // >= 或 >
            getch();
            if(cur_char == '=') {
                cur_symbol = geq;
                getch();
            } else {
                cur_symbol = gtr;
            }
            break;
        case 5: // !=
            getch();
            if(cur_char == '=') {
                cur_symbol = neq;
                getch(); 
            } else {
                cur_symbol = not;
            }
            break;
        case 6: // 其他单个字符的情况
            cur_symbol = ssym[cur_char];
            if(cur_char != '$') getch();
            break;
    }
}

// 初始化
void lex_init()
{
    // 设置单字符号
    for(int i=0; i<256; i++)
        ssym[i] = nul;
    
	ssym['+'] = plus;
	ssym['-'] = minus;
	ssym['*'] = times;
	ssym['/'] = slash;
    ssym['%'] = mod;
    ssym['&'] = and;
    ssym['|'] = or;
	ssym['('] = lparen;
	ssym[')'] = rparen;
    ssym['['] = lpar;
    ssym[']'] = rpar; 
    ssym['{'] = begin;
    ssym['}'] = end;
	ssym[','] = comma;
	ssym['.'] = point;
	ssym[';'] = semicolon;

    // 设置保留字列表
	strcpy(&(word[0][0]),  "if");
	strcpy(&(word[1][0]),  "else");
	strcpy(&(word[2][0]),  "while");
	strcpy(&(word[3][0]),  "for");
	strcpy(&(word[4][0]),  "scan");
	strcpy(&(word[5][0]),  "print");
	strcpy(&(word[6][0]),  "bscan");
	strcpy(&(word[7][0]),  "bprint");
    strcpy(&(word[8][0]),  "bool");
    strcpy(&(word[9][0]),  "int");
    strcpy(&(word[10][0]), "struct");
    strcpy(&(word[11][0]), "void");
	strcpy(&(word[12][0]), "procedure");
	strcpy(&(word[13][0]), "true");
	strcpy(&(word[14][0]), "false");
    strcpy(&(word[15][0]), "main");
    strcpy(&(word[16][0]), "call");
    strcpy(&(word[17][0]), "return");

	wsym[0]  = ifsym;
	wsym[1]  = elsesym;
	wsym[2]  = whilesym;
	wsym[3]  = forsym;
	wsym[4]  = readsym;
	wsym[5]  = writesym;
	wsym[6]  = breadsym;
	wsym[7]  = bwritesym;
	wsym[8]  = boolsym;
	wsym[9]  = intsym;
    wsym[10] = structsym;
    wsym[11] = voidsym;
	wsym[12] = procsym;
	wsym[13] = truesym;
    wsym[14] = falsesym;
    wsym[15] = mainsym;
	wsym[16] = callsym;
    wsym[17] = returnsym;
}

// 获得下一个单词
// 有效信息传递至 cur_sym, cur_num, cur_symbol中
void lex_next()
{    
    // 过滤空格 换行符 制表符
    while(cur_char == ' ' || cur_char == 9 || cur_char == 10)
        getch();

    if(isABC(cur_char)) {        // 当前单词是标识符或保留字 
        set_sym();
        set_symbol(0);
    } else if(is123(cur_char)) { // 当前单词是数字
        set_num();
        set_symbol(1);
    } else if(cur_char == '=') { // 赋值或等于等于
        set_symbol(2);
    } else if(cur_char == '<') { // 小于或小于等于 
        set_symbol(3);
    } else if(cur_char == '>') { // 大于或大于等于 
        set_symbol(4);
    } else if(cur_char == '!') { // 非或不等于
        set_symbol(5);
    } else {                     // 单字符
        set_symbol(6);
    }
} 

// 报错
void panic(char* warning)
{
    char space[81];
    memset(space, ' ', 80);
    space[head - 1] = '\0';
    
    printf("%s^^\n", space);    
    printf("panic failed : %s",warning);
    // printf("cur_symbol = %d\n",cur_symbol);
    exit(1);
}

// 检查和报错
void assert(bool condition, char* warning)
{
    if(!condition) {
        panic(warning);
    }
}